package no.uia.IKT508.XH;

import java.text.SimpleDateFormat;
import java.text.DateFormat;
import java.util.Date;

import no.uia.IKT508.XH.XHTalkDialog.AudioListener;
import no.uia.IKT508.XH.XHTalkDialog.ChatListener;
import no.uia.IKT508.XH.XHVariable.State;

import org.alljoyn.bus.BusAttachment;
import org.alljoyn.bus.BusException;
import org.alljoyn.bus.BusListener;
import org.alljoyn.bus.Mutable;
import org.alljoyn.bus.SessionListener;
import org.alljoyn.bus.SessionOpts;
import org.alljoyn.bus.Status;
import org.alljoyn.bus.annotation.BusSignalHandler;

import android.R.bool;
import android.app.Activity;
import android.app.Application;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.util.Log;


public class XHClient{
	static {
		System.loadLibrary("alljoyn_java");
	}
	
	private static final String TAG = "no.uia.IKT508.XH.Client";
	
	private BusAttachment mBus;
	private ClientHandler mBusHandler;
	private ChatBusListener mBusListener;
	
	//this listener is set as a call back method. When the connection between two applications
	//is finished, the client needs to tell this fact to the main activity. So launch the method OnComplete()
	//inside the listener, the listener in the main activity is invoked to execute the codes 
	//in the higher level
	private XHListener mListerner;
	
	private ChatListener mChatListener;
	
	private AudioListener mAudioListener;
	
	private int mSessionId;
	
	//the established session port
	private short contact_port;
	
	//the well name of current application
	private String well_name;
	
	private Status status;
	
	//the state of the client
	private State mState;
	
	
	
	//get the activity of the main activity to launch the talk activity
	public XHClient(XHListener listener)
	{
		this.mListerner = listener;
		init();
	}
	
	public void setChatListener(ChatListener listener)
	{
		this.mChatListener = listener;
	}
	
	//set the audio listener, when receive the reply from the client
	public void setAudioListener(AudioListener listener)
	{
		this.mAudioListener = listener;
	}
	
	//Initialize the client and start the internal handler
	public void init()
	{
		//set the pre-prepared session port
		contact_port = XHVariable.CONTACT_PORT_1;
		
		HandlerThread handlerThread = new HandlerThread("ClientHandler");
		handlerThread.start();
		mBusHandler = new ClientHandler(handlerThread.getLooper());
		
		//send the command to connect client to the bus
		mBusHandler.sendEmptyMessage(XHVariable.CONNECT);
	}
	
	
	//user press the discover button
	public void startDiscover()
	{
		Message msg = mBusHandler.obtainMessage(XHVariable.DISCOVER);
		mBusHandler.sendMessage(msg);
	}
	
	
	//disconnect the session
	public void startDisconnect()
	{
		Message msg = mBusHandler.obtainMessage(XHVariable.DISCONNECT);
		mBusHandler.sendMessage(msg);
	}
	
	
	public void setWellName(String name)
	{
		well_name = name;
	}
	
	//if the local service has connected to the remote client, change the port to
	//help the client connect to another session
	public void changePort()
	{
		if (XHVariable.CONTACT_PORT_1 == contact_port)
		{
			contact_port = XHVariable.CONTACT_PORT_2;
		}
		else
		{
			contact_port = XHVariable.CONTACT_PORT_1;
		}
	}
	
	
	//the typical method to implement the service/client in alljoyn, creating a handler
	//independent of main UI thread to handle the procedure
	class ClientHandler extends Handler
	{
		public ClientHandler (Looper looper)
		{
			super(looper);
		}
		
		@Override
		public void handleMessage(Message msg)
		{
			switch (msg.what)
			{
			case XHVariable.CONNECT:
			{
				doConnect();
				break;
			}
			case XHVariable.DISCOVER:
			{
				doDiscover();
				break;
			}
			case XHVariable.JOIN:
			{
				doJoin((String)msg.obj);
				break;
			}
			case XHVariable.DISCONNECT:
			{
				doDisconnect();
				break;
			}
			case XHVariable.JOINED:
			{
				mListerner.OnComplete((String)msg.obj, 0);
				break;
			}
			default:
				break;
			}
		}
		
		//this iface name must be the same with the name of defined interface, that is the package prefix 
		//plus the name of bus interface
		@BusSignalHandler(iface="no.uia.IKT508.XH.XHChatInterface", signal="chat")
		public void chatHandler (String msg) throws BusException
		{
			//get the time and integrate it with the message
			DateFormat dateFormat = new SimpleDateFormat("DD:HH:mm");
			Date date = new Date();
			
			String tmp = dateFormat.format(date) + ": " + msg;
			
			//invoke the method OnComplete of ChatListener to send the message up to the higher level class
			mChatListener.OnComplete(tmp, 0);
		}
		
		
		//audio handler
		@BusSignalHandler(iface="no.uia.IKT508.XH.XHChatInterface", signal="audio")
		public void audioHandler(byte[] sound, int size) throws BusException
		{
			mAudioListener.OnComplete(sound, size);
		}
		
		//be informed to start the audio
		@BusSignalHandler(iface="no.uia.IKT508.XH.XHChatInterface", signal="startAudioTalk")
		public void startAudioHandler(String start, boolean value, boolean valueRun) throws BusException
		{
			mAudioListener.OnStart(start, value, valueRun);
		}
		
		
		//connect to the bus
		public void doConnect()
		{
			mBus = new BusAttachment(getClass().getName(), BusAttachment.RemoteMessage.Receive);
			
			//connect to the bus
			status = mBus.connect();
			if (Status.OK != status)
			{
				XHVariable.logStatus(TAG, "Failed to connect to the bus!", status);
				return;
			}
			
			//register the signal handler for the bus signal
			status = mBus.registerSignalHandlers(this);
			if (Status.OK != status)
			{
				XHVariable.logStatus(TAG, "Failed to register the bus signal handler!", status);
				return;
			}
			
			// listen to the advertised name
			mBusListener =  new ChatBusListener();
			mBus.registerBusListener(mBusListener);
			
			mState = State.ATTACHMENTED;
		}
		
		//start discover the service
		public void doDiscover()
		{
			//start to discover
			status = mBus.findAdvertisedName(XHVariable.NAME_PREFIX);
			if (Status.OK != status)
			{
				XHVariable.logStatus(TAG, "Client:findAdvertiseName()", status);
				return;
			}
			
			mState = State.DISCOVERING;
		}
		
		//find the advertisement and join this session
		public void doJoin(String name)
		{
			SessionOpts sessionOpts = new SessionOpts();
			Mutable.IntegerValue sessionId = new Mutable.IntegerValue();
			mSessionId = sessionId.value;
			
			//join the session
			status = mBus.joinSession( name, 
									   contact_port,
									   sessionId, 
									   sessionOpts, 
									   new SessionListener(){
				@Override
				public void sessionLost(int sessionId) {
	                Log.i(TAG, "BusListener.sessionLost(" + sessionId + ")");
	            }
			});
			
			if (Status.OK == status)
			{
				//when the client sent the request to service to join the session, we regard it as 
				//succeeding connect to the remote service, so invoke the method OnComplete() in 
				//the Listener as a callback
				//don't execute too long codes in the callback method,
				//use the message to execute them in the handler
				Message msg = mBusHandler.obtainMessage(XHVariable.JOINED, "");
				mBusHandler.sendMessage(msg);
			}
		}
		
		public void doDisconnect()
		{
			mBus.leaveSession(mSessionId);
			mBus.unregisterBusListener(mBusListener);
			mBus.unregisterSignalHandlers(this);
			mBus.disconnect();
			getLooper().quit();
		}
	}
	
	private class ChatBusListener extends BusListener
	{
		@Override
		public void foundAdvertisedName(String Name, short transport, String namePrefix)
		{
			//it is not supposed to join the local session created by the local service
			if (false == well_name.equals(Name))
			{
				Message msg = mBusHandler.obtainMessage(XHVariable.JOIN, Name);
				mBusHandler.sendMessage(msg);
			}
		}
	}
}